package DensityObjects
{
	import TaskStructure.DensityItem;
	import flash.display.Shape;
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	/** And object of this class can be dragged around on screen; however, the speed of dragging is determined
	 * by the mass of the object.
	 */ 
	public class DroppableMC extends MovieClip
	{
		public static const G:Number = 980/10; // cm/s^2 straight fiziks dogg
		public static const DRAG_C:Number = 1;
		// variables acting as constants
		private static var X_LIMIT:Number, Y_LIMIT:Number;
		
		// private variables for local use.
		private var _initpoint:Point = null, _refpoint:Point;
		private var _fps:Number, _frame=uint;
		private var _dropSpeedCMpS:Number;
		private var _previousSpeedCMpS:Number;
		private var _impact:Boolean = false;
		private var _densityShape:Shape;
		
		// private variables accessible by getter/setter
		private var _dropping:Boolean = false;
		public function get isDropping():Boolean{return _dropping;}
				
		private var _mass:Number, _height;
		/** Mass can range from 0 to 1, such that 0 means move at full speed and 1 means unmovable */
		public function get mass():Number{return _mass;}
		public function set mass(m:Number):void{_mass = m;}
		private var _volume:Number;
		/** Volume can range from 0 to 1, such that 0 means no volume, 1 means fills the entire space of the screen */
		public function get volume():Number{return _volume;}
		public function set volume(v:Number):void{_volume = v;}
		// density cannot be set directly.  This class corresponds to objects, not materials.
		public function get density():Number{return mass/volume;}
		
		public function get pxHeight():Number{return _height;}
		public function set pxHeight(h:Number):void{_height=h;}
		
		private var _currentPercentInFluid:Number = 0;
		public function get currentPercentInFluid ():Number{return _currentPercentInFluid;}
			
		/** Give mass in grams, volume in cm^3 */
		public function DroppableMC(mass:Number=0, volume:Number=0)
		{
			// set up volume, mass, density
			this._height = this.height;
			this._mass = mass;
			this._volume = volume;
			this.addEventListener( Event.ADDED_TO_STAGE, handleAdded);
		}
		private function handleAdded (event:Event):void
		{
			this.removeEventListener( Event.ADDED_TO_STAGE, handleAdded);
			X_LIMIT = this.parent.width;
			Y_LIMIT = this.parent.height;
			_fps = this.parent.stage.frameRate;
			this.mouseEnabled = false;
			
		}
		
			/** On initiating this object set the initial point */
		public function set initPoint(ip:Point):void{_initpoint = ip; setupNewDrop();}	
		
		/** This method is used to show a density representation of the object.  Darker means denser */
		public function showDensity ():void
		{
			if (_densityShape == null || _densityShape.parent != this)
			{
				_densityShape = new Shape();
				_densityShape.graphics.beginFill(0x000000, density/10);
				_densityShape.graphics.drawRect(0, 0, DensityItem.CM_TO_PX, DensityItem.CM_TO_PX);
				_densityShape.graphics.endFill();
				_densityShape.x = (this.width - _densityShape.width)/2;
				_densityShape.y = (this.height - _densityShape.height)/2;
				this.addChild (_densityShape);
			}
			
		}
		/** Hide density object */
		public function hideDensity ():void
		{
			if (_densityShape != null && _densityShape.parent == this)
			{
				this.removeChild(_densityShape);
			}
		}
		
		/** This function is used to setup a new flick of the object */
		private function setupNewDrop ():void
		{
			_dropping = false;
			_dropSpeedCMpS = 0;
			_frame = 0;
			this.x = _initpoint.x;
			this.y = _initpoint.y;
		}
		
		/** Start drop, but only if we are currently not dropping */
		public function drop():void
		{
			if (!_dropping && _initpoint != null)
			{
				_refpoint = new Point(_initpoint.x, _initpoint.y);
				_dropping = true;
				this.addEventListener( Event.ENTER_FRAME, handleFrame);
			}
		}
		/** When user has pressed mouse down this object can be flicked.
		 * Detect a change in mouse position whose distance exceeds some threshold.  Then move in proportion to dx,dy and mass.
		 */ 
		private function handleFrame (event:Event):void
		{
			_frame++;
			// are we currently dropping?
			if (isDropping)
			{
				// calculate time since last frame;
				var time:Number = 1/_fps;
				var _dropSpeedPXpF:Number = _dropSpeedCMpS * _fps / DensityItem.CM_TO_PX;
				
				// get density at top of object, get density at bottom of object
				var dtop:Number = Beaker(parent).densityAtPoint(new Point (this.x, this.y));
				var dbottom:Number = Beaker(parent).densityAtPoint(new Point (this.x, this.y+pxHeight*2/4));
				// calculate weight of water displaced
				_currentPercentInFluid = Beaker(parent).percentUnderFluid(this);
				trace("percent in water", _currentPercentInFluid);
				var waterf:Number = _volume*dbottom*_currentPercentInFluid*G;
				
				// calculate force of gravity
				var gravf:Number = _mass*G;
				
				// Opposite forces, get difference
				var downf:Number = gravf - waterf;
				
				// brace for impact
				if (dbottom != 0 && !_impact)
				{
					_impact = true;
					// cut speed in half
					_dropSpeedCMpS /= 2;
				} else if (dbottom == 0 && _impact)
				{ // if it pops out of the water
					_impact = false;
				}
				
				
				// calculate drag  D=1/2*C*p*A*v^2
				// where C is a drag coefficient, p is fluid density, A is cross-sectional area, v is velocity
				var dragf:Number;
				if (_dropSpeedCMpS > 0)
				{
					dragf = DRAG_C * .5 * dbottom * Math.pow(Math.pow(_volume, 1/3),2)*_dropSpeedCMpS*_dropSpeedCMpS;
					// apply drag force in opposite direction of
					
				} else
				{
					dragf = DRAG_C * .5 * dtop * Math.pow(Math.pow(_volume, 1/3),2)*_dropSpeedCMpS*_dropSpeedCMpS;
					// apply drag force in opposite direction of
					
				}
				
				/*				
				if (dtop == 0 && dbottom == 0)
				{
					trace("AIR -> down", downf, "water", waterf, "drag", dragf, _dropSpeedCMpS);
				
				} else if (dtop == 0 && dbottom == 1)
				{
					trace("ENTERING -> down", downf, "water", waterf, "drag", dragf, _dropSpeedCMpS);
				} else if (dtop == 1 && dbottom == 1)
				{
					trace("WATER -> down", downf, "water", waterf, "drag", dragf, _dropSpeedCMpS);
				} else if (dtop == 0 && dbottom == 1)
				{
					trace("LEAVING -> down", downf, "water", waterf, "drag", dragf, _dropSpeedCMpS);
				}else if (dbottom > 100)
				{
					trace("BOTTOM -> down", downf, "water", waterf, "drag", dragf, _dropSpeedCMpS);
				}
				*/
				
				if (_dropSpeedCMpS > 0)
				{
					downf -= dragf;
				} else
				{
					downf += dragf;
				}
				trace("Forces: gravity", gravf, "water", waterf, "drag", dragf, "resulting", downf);
				// acceleration in sum direction
				var acc:Number = downf / _mass;
				
				// calculate new distance from reference point y in cm
				var ny_cm:Number = _dropSpeedCMpS*time + .5*acc*time*time;
				var ny_px:Number = Math.round(ny_cm * DensityItem.CM_TO_PX);
				
				// only allow continue if we're not touching bottom
				if (dbottom < 100)
				{
					this.y += ny_px;
				}
				// update speed
				_previousSpeedCMpS = _dropSpeedCMpS;
				_dropSpeedCMpS = ny_cm/time;
				if (_impact && Math.abs(_dropSpeedCMpS) < .1 && Math.abs(_previousSpeedCMpS) < .1 || _dropSpeedCMpS == -Infinity)
				{
					this.removeEventListener( Event.ENTER_FRAME, handleFrame);
					_dropping = false;
				}
				//trace("speed", _dropSpeedCMpS, _previousSpeedCMpS, Math.abs(_dropSpeedCMpS+_previousSpeedCMpS),_dropSpeedCMpS/Math.abs(_dropSpeedCMpS) != _previousSpeedCMpS/Math.abs(_previousSpeedCMpS) );
				//trace("dtop", dtop, "dbottom", dbottom, "waterf", waterf, "mass", _mass, "gravf", gravf, "downf", downf, "acc", acc, "new y", ny);
				
				
				_refpoint = new Point(this.x, this.y);	
			} 	
		}
	}
}